/*
 * Copyright 2012 Finbudget.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package finbudgettable.client.ui.grid.cell.attachment;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FocusPanel;
import finbudget.client.ui.widget.upload.UploadWidget;
import finbudgettable.client.ui.grid.cell.AbstractPopupEditor;
import finbudgettable.client.ui.grid.event.ValueChangedEvent;

import static finbudget.client.ui.widget.upload.UploadWidget.UploadWidgetFactory;
import static finbudget.client.ui.widget.upload.UploadWidget.UploadWidgetParams;

public class AttachmentCellEditor extends AbstractPopupEditor<UploadWidget.UploadWidgetParams> {

    interface Strings extends Constants {
        @DefaultStringValue("Закрыть?")
        String closeQuestion();
    }

    private static final Strings STRINGS = GWT.create(Strings.class);

    private UploadWidgetParams value;
    private final FocusPanel focusPanel;
    private final UploadWidgetFactory factory;
    private UploadWidget uploadWidget;

    public AttachmentCellEditor(UploadWidgetFactory factory) {
        super(false, true);
        this.factory = factory;
        focusPanel = new FocusPanel();
        add(focusPanel);
    }

    private UploadWidgetParams getParams() {
        UploadWidgetParams params = new UploadWidgetParams();
        params.keyNode = value.keyNode;
        params.value = uploadWidget.getStringValue();
        if (params.value != null) {
            params.value = params.value.trim();
        }
        return params;
    }

    @Override
    protected void onPreviewNativeEvent(Event.NativePreviewEvent event) {
        int type = event.getTypeInt();
        if (type == Event.ONCLICK) {
            int x = event.getNativeEvent().getClientX();
            int y = event.getNativeEvent().getClientY();
            if (isCursorOutOfBounds(x, y)) {
                if (tryToClose()) {
                    repeatClickEvent(event.getNativeEvent());
                }
            }
        } else if (Event.ONKEYDOWN == event.getTypeInt()) {
            NativeEvent nativeEvent = event.getNativeEvent();
            int keyCode = nativeEvent.getKeyCode();
            if (keyCode == KeyCodes.KEY_UP
                    || keyCode == KeyCodes.KEY_RIGHT
                    || keyCode == KeyCodes.KEY_LEFT
                    || keyCode == KeyCodes.KEY_DOWN
                    || keyCode == KeyCodes.KEY_HOME
                    || keyCode == KeyCodes.KEY_END) {
                if (tryToClose()) {
                    repeatKeyDownEvent(getLastParent(), keyCode);
                    nativeEvent.preventDefault();
                }
            }
        } else if (Event.ONKEYUP == event.getTypeInt()) {
            int keyCode = event.getNativeEvent().getKeyCode();
            if (keyCode == KeyCodes.KEY_ESCAPE) {
                tryToClose();
            }
        }
    }

    @Override
    public void show() {
        super.show();
        value.readonly = isReadonly();
        uploadWidget = factory.create(value);
        focusPanel.setWidget(uploadWidget);
    }

    private boolean isCursorOutOfBounds(int x, int y) {
        return !(x > getElement().getAbsoluteLeft()
                && x < getElement().getAbsoluteRight()
                && y > getElement().getAbsoluteTop()
                && y < getElement().getAbsoluteBottom());
    }

    /**
     * @return true if panel has been closed
     */
    private boolean tryToClose() {
        if (uploadWidget.canClose()) {
            fireEvent(new ValueChangedEvent<UploadWidgetParams>(getParams()));
            hide();
            return true;
        } else if (Window.confirm(STRINGS.closeQuestion())) {
            uploadWidget.terminate();
            fireEvent(new ValueChangedEvent<UploadWidgetParams>(getParams()));
            hide();
            return true;
        }
        return false;
    }

    private void repeatClickEvent(final NativeEvent event) {
        if (BrowserEvents.CLICK.equals(event.getType()) && Element.is(event.getEventTarget())) {
            final Element el = event.getEventTarget().cast();
            Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
                @Override
                public void execute() {
                    el.dispatchEvent(Document.get().createClickEvent(0,
                            event.getScreenX(), event.getScreenY(),
                            event.getClientX(), event.getClientY(),
                            false, false, false, false));
                }
            });
        }
    }

    private void repeatKeyDownEvent(final Element parent, final int keyCode) {
        Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() {
            @Override
            public void execute() {
                parent.dispatchEvent(Document.get().createKeyDownEvent(false, false, false, false, keyCode));
            }
        });
    }

    @Override
    public void setValue(UploadWidgetParams value) {
        this.value = value;
    }

    public void focus() {
        focusPanel.setFocus(true);
    }

	@Override
	public void proceedSettingReadonly() {
	}

}
